package cn.jeina.utils.jcommander;

import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.exceptions.InvocationTargetRuntimeException;
import cn.hutool.core.exceptions.NotInitedException;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.ServiceLoaderUtil;
import cn.hutool.core.util.StrUtil;
import cn.jeina.utils.jcommander.utils.UsageUtil;
import cn.jeina.utils.validator.AutoValidClose;
import cn.jeina.utils.validator.ValidatorUtils;
import com.beust.jcommander.JCommander;
import com.beust.jcommander.ParameterException;
import lombok.extern.slf4j.Slf4j;

import javax.validation.ValidationException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 启动器 该启动器会根据spi自动发现jcommander的服务. 根据反射发现参数类型, 执行启动service 启动器还提供了基础的参数校验, 异常处理, 打印help信息等. 然后
 *
 * @author zhyu
 * @version 1.0
 * @date 2022-7-30 22:10
 */
@SuppressWarnings("ALL")
@Slf4j
public class JcommanderStarter {

      /**
       * 启动器启动
       *
       * @param args main方法的args
       */
      public static void start(String[] args) {
            boolean needUsage = false;
            boolean showParams = false;
            BaseParameter params = null;
            JCommander jCommander = null;
            try {
                  JcommanderService service = ServiceLoaderUtil.loadFirstAvailable(JcommanderService.class);
                  if (null == service) {
                        throw new NotInitedException(
                            "spi没有找到服务, 请查看是否在classpath下面存在文件 META-INF.services/cn.changeforyou.utils.jcommander.JcommanderService, 并且里面包含了具体服务的完全限定类名");
                  }
                  Class<? extends BaseParameter> parameterClass = findAndValidParameterClass(service);

                  //组装参数 校验
                  params = parameterClass.getDeclaredConstructor().newInstance();
                  jCommander = JCommander.newBuilder()
                      .addObject(params)
                      .build();
                  Method parse = ReflectUtil.getMethod(JCommander.class, "parse", boolean.class, String[].class);
                  ReflectUtil.invoke(jCommander, parse, false, args);
                  if (!params.isHelp()) {
                        service.beforeValidate();
                        AutoValidClose annotation = AnnotationUtil.getAnnotation(params.getClass(), AutoValidClose.class);
                        if (null == annotation) {
                              ValidatorUtils.valid(params);
                        }
                        service.execute_do(params);
                  } else {
                        needUsage = true;
                  }
            } catch (ParameterException e) {
                  String message = e.getMessage();
                  if (StrUtil.isNotBlank(message) && message.endsWith("multiple times")) {
                        log.error("error: 参数类的Parameter注解有误, names数组中有个命名出现多次, 请修改:\n{}", e.getMessage());
                  } else if (StrUtil.isNotBlank(message) && message.endsWith(" but no main parameter was defined in your arg class")) {
                        String paramName = parseErrorParamName(message);
                        if (StrUtil.isNotBlank(paramName)) {
                              log.error("jcommander不能输入没定义的参数, 名称: {},\n 请输入合法参数, -h, --h, -help, --help 查看帮助", paramName);
                        } else {
                              deafaultError("error: {}", "请输入合法参数, -h, --h, -help, --help 查看帮助");
                        }
                        needUsage = true;
                        showParams = true;
                  } else if (StrUtil.isNotBlank(message) && message.startsWith("Invalid value for")) {
                        log.error("参数输入不对:\n{}", e.getMessage());
                  } else {
                        deafaultError("error: {}", e.getMessage());
                        needUsage = true;
                        showParams = true;
                  }
            } catch (ValidationException e) {
                  log.error("error: {}", e.getMessage());
                  log.error("请输入合法参数, -h, --h, -help, --help 查看帮助");
                  needUsage = true;
                  showParams = true;
            } catch (InvocationTargetRuntimeException e){
                  Throwable cause = e.getCause();
                  boolean catchIt = false;
                  if(cause != null){
                        Throwable cause1 = cause.getCause();
                        if(null != cause1){
                              Pattern compile = Pattern.compile("Can only specify option -\\w* once.");
                              String message = cause1.getMessage();
                              if (compile.matcher(message).find()) {
                                    catchIt = true;
                              }
                        }
                  }
                  if(catchIt){
                        log.error("参数只能输入一次, -h, --h, -help, --help 查看帮助");
                  }else{
                        log.error("error", e);
                        log.error("请输入合法参数, -h, --h, -help, --help 查看帮助");
                  }
                  needUsage = true;
                  showParams = true;
            }catch (Exception e) {
                  log.error("error", e);
                  log.error("请输入合法参数, -h, --h, -help, --help 查看帮助");
                  needUsage = true;
                  showParams = true;
            }
            if(showParams){
                  log.error("你输入的参数是:{}", Arrays.toString(args));
            }
            if(needUsage){
                  UsageUtil.chineseUsage(params, jCommander);
            }
      }


      private static void deafaultError(String s, String o) {
            log.error(s, o);
      }

      private static String parseErrorParamName(String message) {
            Pattern pattern = Pattern.compile("\\.*?'(\\S+)' but no main parameter was defined in your arg class");
            Matcher matcher = pattern.matcher(message);
            if (matcher.find()) {
                  String group = matcher.group(1);
                  return group;
            }
            return null;
      }

      /**
       * 发现并校验参数类型
       *
       * @param service
       * @return
       */
      private static Class<? extends BaseParameter> findAndValidParameterClass(JcommanderService service) {
            Class<? extends JcommanderService> serviceImplClass = service.getClass();
            Class parameterClass = null;
            for (Type genericInterface : serviceImplClass.getGenericInterfaces()) {
                  ParameterizedType type = (ParameterizedType) genericInterface;
                  if (type.getRawType() == JcommanderService.class) {
                        Type[] actualTypeArguments = type.getActualTypeArguments();
                        parameterClass = (Class) actualTypeArguments[0];
                        break;
                  }
            }
            if (null == parameterClass) {
                  log.error("寻找parameterClass失败!!! 退出");
                  throw new NotInitedException("初始化失败, 请查看代码Service是否提供了参数的泛型信息");
            }
            if (log.isDebugEnabled()) {
                  log.debug("寻找到parameterClass: {}", parameterClass.getCanonicalName());
            }
            return parameterClass;
      }
}
